﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Storage.EntityModels;
using Stats;
using Stats.Objects;
using Storage;
using System.Drawing;

namespace Security 
{
    public class Profile
    {
        #region INSTANCE_VARIABLE

        // fields
        string _Address;
        string _AvatarPath;
        int? _Age;
        DateTime? _Birthday;
        string _City;
        bool? _DrivingLicence;
        string _Email; 
        Gender _Gender;
        int _Id;
        string _Name;
        string _PhoneNumber;
        Smoker _Smoker;
        string _Surname;
        string _Username;
        UserPreferences _UserPreference;

        // lists
        List<Profile> _Banned = null;
        List<Feedback> _Feedbacks = null;
        List<Profile> _BuddiesList = null;
        List<String> _Interests = null;
        List<SocialNetwork> _SocialNetworks = null;
        List<Users_VehicleDetails> _UsersVehiclesDetails = null;
        List<VehicleDetails> _VehiclesDetails = null;


        // filtering data
        Dictionary<string, PrivacyLevel> userData;
        Dictionary<string, PrivacyLevel> modifiedLevels = new Dictionary<string, PrivacyLevel>();
        PrivacyLevel privacyLevel;

        // data repository
        private const string defaultMalePath = "../master/resourcesNew/img/avatar_male.png";
        private const string defaultFemalePath = "../master/resourcesNew/img/avatar_female.png";
        private const string defaultUnknownPath = "../master/resourcesNew/img/avatar_unk.png";

        #endregion

        #region CONSTRUCTOR

        /// <summary>
        /// Public constructor
        /// </summary>
        /// <param name="user">The Storage.EntityModels that rapresent that user</param>
        /// <param name="userData">The Privacy Levels associated to each fields</param>
        /// <param name="level">The filter parameter</param>
        public Profile(Storage.EntityModels.User user, Dictionary<string, PrivacyLevel> userData, PrivacyLevel level)
        {
            // copyin'...
            _Address = user.Address;
            _AvatarPath = user.AvatarPath;
            _Birthday = user.Birthday;
            _City = user.City;
            _DrivingLicence = user.DrivingLicence;
            _Email = user.Email; 
            _Gender = user.Gender;
            _Name = user.Name;
            _PhoneNumber = user.PhoneNumber;
            _Smoker = user.Smoker;
            _Surname = user.Surname;
            _Age = user.Age;
            _Id = user.Id;
            _Username = user.Username;
            _UserPreference = user.UserPreference;

            // loadin' SNs
            _SocialNetworks = new List<SocialNetwork>();

            foreach (SocialNetworkSubscribeDetails sn in user.SubscribedSocialNetworks.ToList())
                _SocialNetworks.Add(sn.SocialNetwork);
            
            // loadin' interests
            _Interests = new List<string>();

            foreach (Users_Interest i in user.Interests.ToList())
                _Interests.Add(i.InterestDetails.Description);


            this.userData = userData;
            this.privacyLevel = level;
        }

        #endregion

        #region PUBLIC_PROPERTIES

        /// <summary>
        /// Gets or sets the address of the profile
        /// </summary>
        public string Address
        {
            get
            {
                if (userData["Address"] >= privacyLevel)
                    return _Address;
                else
                    return null;
            }
            set
            {
                _Address = value;
            }
        }

        /// <summary>
        /// Gets the Age of the user (calculated from the Birthdate)
        /// </summary>
        public int Age
        {
            get
            {
                if (userData["Age"] >= privacyLevel)
                    if (_Age == null)
                        return -1;
                    else
                        return (int)_Age;
                else
                    return -1;
            }

        }

        /// <summary>
        /// Gets or sets the path of the avatar to display
        /// </summary>
        public string AvatarPath
        {
            get
            {
                if (userData["AvatarPath"] >= privacyLevel && _AvatarPath != null)
                {
                    return _AvatarPath;
                }
                else
                {
                    return GetDefaultAvatar();
                }
            }
            set
            {
                if (value != string.Empty) 
                    _AvatarPath = value;
            }
        }

        /// <summary>
        /// Get the list of the banned users
        /// </summary>
        public List<Profile> BannedList
        {
            get
            {
                if (userData["BuddyLists"] >= privacyLevel) 
                {
                    if (_Banned == null)
                        loadBannedList();

                    return _Banned;
                }
                
                return null;
            }
        }

        /// <summary>
        /// Gets or sets the birthdate of the profile
        /// </summary>
        public DateTime? Birthday
        {
            get
            {
                if (userData["Birthday"] >= privacyLevel)
                    return _Birthday;
                else
                    return null;
            }
            set
            {
                _Birthday = value;
            }
        }

        /// <summary>
        /// Gets the list of friend
        /// </summary>
        public List<Profile> BuddyList
        {
            get
            {
                if (userData["BuddyLists"] >= privacyLevel) 
                {
                    if (_BuddiesList == null)
                        loadBuddyList();

                    return _BuddiesList;
                }

                return null;
            }
        }

        /// <summary>
        /// Get or sets the city of the profile
        /// </summary>
        public string City
        {
            get
            {
                if (userData["City"] >= privacyLevel)
                    return _City;
                else
                    return null;
            }
            set
            {
                _City = value;
            }
        }

        /// <summary>
        /// Get or sets if user has a driving licence
        /// </summary>
        public bool DrivingLicence
        {
            get
            {
                if (userData["DrivingLicence"] >= privacyLevel)
                {
                    if (_DrivingLicence != null)
                        return (bool)_DrivingLicence;
                    else
                        return false;
                }
                else
                    return false;
            }
            set
            {
                _DrivingLicence = value;
            }
        }

        /// <summary>
        /// Get or sets the e-mail of the profile
        /// </summary>
        /// <remarks>Email field belongs to Asp table</remarks>
        public string Email
        {
            get
            {
                if (userData["Email"] >= privacyLevel)
                    return _Email;
                else
                    return string.Empty;
            }
            set
            {
                _Email = value;
            }
        }

        /// <summary>
        /// Get the privacy level associated to each field
        /// </summary>
        public Dictionary<string, PrivacyLevel> FieldTable
        {
            get 
            { 
                //return userData; 
                return modifiedLevels;
            }
        }

        /// <summary>
        /// Gets or sets the gender of the profile
        /// </summary>
        public Gender Gender
        {
            get
            {
                if (userData["Gender"] >= privacyLevel)
                    return (Gender)_Gender;
                else
                    return Gender.Unknown;
            }
            set
            {
                _Gender = value;
            }
        }

        /// <summary>
        /// Gets the unique identificator of the profile inside the DB
        /// </summary>
        public int Id
        {
            get
            {
                return _Id;
            }
        }

        /// <summary>
        /// Gets the list of interests
        /// </summary>
        public List<string> Interests
        {
            get
            {
                if (userData["Interests"] >= privacyLevel)
                    return _Interests; //already loaded in constructor
                else
                    return null;
            }
        }

        /// <summary>
        /// Gets or sets the name of the profile
        /// </summary>
        public string Name
        {
            get
            {
                if (userData["Name"] >= privacyLevel)
                    return _Name;
                else
                    return null;
            }
            set
            {
                _Name = value;
            }
        }

        [Obsolete("The Nickname property is deprecated, use Username instead",false)]
        public string Nickname
        {
            get
            {
                if (userData["Username"] >= privacyLevel)
                    return _Username;
                else
                    return null;
            }
        }

        /// <summary>
        /// Gets or sets the phone number of the profile
        /// </summary>
        public string PhoneNumber
        {
            get
            {
                if (userData["PhoneNumber"] >= privacyLevel)
                    return _PhoneNumber;
                else
                    return null;
            }
            set
            {
                _PhoneNumber = value;
            }
        }

        /// <summary>
        /// Gets or sets if user is a smoker
        /// </summary>
        public Smoker Smoker
        {
            get
            {
                if (userData["Smoker"] >= privacyLevel)
                    return _Smoker;
                else
                    return Smoker.Unknown;
            }
            set
            {
                _Smoker = value;
            }
        }

        /// <summary>
        /// Gets the list of social networks subribed by the user
        /// </summary>
        public List<SocialNetwork> SocialNetworks
        {
            get
            {
                if (userData["SocialNetworks"] >= privacyLevel)
                    return _SocialNetworks; // already loaded in constructor
                else
                    return null;
            }
        }

        /// <summary>
        /// Gets or sets the surname of the profile
        /// </summary>
        public string Surname
        {
            get
            {
                if (userData["Surname"] >= privacyLevel)
                    return _Surname;
                else
                    return null;
            }
            set
            {
                _Surname = value;
            }
        }

        /// <summary>
        /// Gets or sets the username of the profile
        /// </summary>
        public string Username
        {
            get
            {
                if (userData["Username"] >= privacyLevel)
                    return _Username;
                else
                    return null;
            }
            set
            {
                _Username = value;
            }
        }

        /// <summary>
        /// Gets the detail of the vehicle
        /// </summary>
        public List<Storage.EntityModels.Users_VehicleDetails> UsersVehiclesDetails
        {
            get
            {
                if (_UsersVehiclesDetails == null)
                {
                    _UsersVehiclesDetails = (from u_vd in StorageManager.Instance.Entities.Users_VehicleDetails
                                             where u_vd.UserId == Id
                                             select u_vd).ToList();
                }
                return _UsersVehiclesDetails;
            }
        }

        /// <summary>
        /// Gets the generic vehicle details
        /// </summary>
        public List<Storage.EntityModels.VehicleDetails> VehiclesDetails
        {
            get
            {
                if (userData["Vehicles"] >= privacyLevel && _VehiclesDetails == null)
                {
                    _VehiclesDetails = (from u_vd in StorageManager.Instance.Entities.Users_VehicleDetails
                                        where u_vd.UserId == Id
                                        select u_vd.VehicleDetails).ToList();
                }
                return _VehiclesDetails;
            }
        }

        #region FEEDBACK_USING_STATS_MODULES

        /// <summary>
        /// Gets the feedback of the profile
        /// </summary>
        public List<Feedback> Feedbacks
        {
            get
            {
                if (userData["Feedbacks"] >= privacyLevel && _Feedbacks == null)
                {
                    List<int> bl = StorageManager.Instance.GetFriendsByUserId(_Id);
                    _Feedbacks = StatsModule.getUserFeedbackList(_Id);

                    // bugfix: control if the user has feedback
                    if (_Feedbacks != null)
                    {
                        foreach (Feedback f in _Feedbacks)
                        {
                            if (!bl.Contains(f.senderID)) f.senderNickname = "";
                        }
                    }
                }
                return _Feedbacks;
            }
        }

        /// <summary>
        /// Gets the feedback as driver of the profile
        /// </summary>
        public AsDriverFeedback DriverFeedback
        {
            get
            {
                if (userData["Feedbacks"] >= privacyLevel)
                    return StatsModule.getAsDriverFeedback(_Id);
                else
                    return null;
            }
        }

        /// <summary>
        /// Gets the feedback as passenger of the profile
        /// </summary>
        public AsPassengerFeedback PassengerFeedback
        {
            get
            {
                if (userData["Feedbacks"] >= privacyLevel)
                    return StatsModule.getAsPassengerFeedback(_Id);
                else
                    return null;
            }
        }

        /// <summary>
        /// Gets the feedback as general of the profile
        /// </summary>
        public GeneralFeedback GeneralFeedback
        {
            get
            {
                if (userData["Feedbacks"] >= privacyLevel)
                    return StatsModule.getGeneralFeedback(_Id);
                else
                    return null;
            }
        }

        #endregion FEEDBACK_USING_STATS_MODULES

        #endregion PUBLIC_PROPERTIES

        #region PUBLIC_METHODS

        /// <summary>
        /// Get or sets the privacylevel of a given field
        /// </summary>
        /// <param name="field">name of field (ex: Name)</param>
        /// <returns>the privacylevel of a given field</returns>
        public PrivacyLevel this[string field]
        {
            get
            {
                return userData[field];
            }
            set
            {
                if (!modifiedLevels.ContainsKey(field))
                    modifiedLevels.Add(field, value);
                else
                    modifiedLevels[field] = value;
            }
        }

        /// <summary>
        /// Indicates wheter the field is visible given the current PrivacyLevel
        /// </summary>
        /// <param name="field">name of field (ex: Name)</param>
        /// <returns>true if field is visible give the current privacylevel, false otherwise</returns>
        public bool isVisible(string field)
        {
            bool chk = userData[field] >= privacyLevel;
            return chk;
        }

        /// <summary>
        /// Check if a user is friend of an other user
        /// </summary>
        /// <param name="otherUserId">The ID of the other user</param>
        /// <returns>True if user and the other user are friends, false otherwise</returns>
        public bool IsFriendOf(int otherUserId)
        {
            List<int> tmp = StorageManager.Instance.GetFriendsByUserId(_Id);
            return tmp.Contains(otherUserId);
        }

        /// <summary>
        /// Check if a user is friend of an other user
        /// </summary>
        /// <param name="otherUserName">The username of the other user</param>
        /// <returns>True if user and the other user are friends, false otherwise</returns>
        public bool IsFriendOf(string otherUserName)
        {
            int u = StorageManager.Instance.GetUserIdByUserName(otherUserName);
            return IsFriendOf(u);
        }

        /// <summary>
        /// Check if the current user has banned an other user
        /// </summary>
        /// <param name="userId">The ID of the other user to check</param>
        /// <returns>True if the passed user is banned, false otherwise</returns>
        public bool HasBanned(int userId) 
        {
             if (_Banned == null) loadBannedList();

             foreach (Profile p in _Banned)
             {
                 if (p.Id == userId) return true;
             }

             return false;
        }

        /// <summary>
        /// Check if the current user has banned an other user
        /// </summary>
        /// <param name="username">The username of the other user to check</param>
        /// <returns>True if the passed user is banned, false otherwise</returns>
        public bool HasBanned(string username)
        {
            if (_Banned == null) loadBannedList();

            foreach (Profile p in _Banned)
            {
                if (p.Username == username) return true;
            }

            return false;
        }

        /// <summary>
        /// Check if the user has been banned from the user specified in the parameter
        /// </summary>
        /// <param name="userId">The ID of the other user</param>
        /// <returns>True if the user is banned, false otherwise</returns>
        public bool IsBannedBy(int userId)
        {
            // I must reload the list because maybe user isn't allowed to see the banned list...
            List<int> ban = StorageManager.Instance.GetBannedUsersByUserId(userId);
            return ban.Contains(this._Id);
        }

        /// <summary>
        /// Check if the user has been banned from the user specified in the parameter
        /// </summary>
        /// <param name="username">The username of the other user</param>
        /// <returns>True if the user is banned, false otherwise</returns>
        public bool IsBannedBy(string username)
        {
            int u = StorageManager.Instance.GetUserIdByUserName(username);
            // I must reload the list because maybe user isn't allowed to see the banned list...
            List<int> ban = StorageManager.Instance.GetBannedUsersByUserId(u);
            return ban.Contains(this._Id);
        }

        /// <summary>
        /// Add a social network to the current profile
        /// </summary>
        /// <param name="SocialNetworkName">The name of the social network</param>
        /// <param name="Token">The user token</param>
        /// <remarks>No one has tested this method...</remarks>
        public void AddSocialNetworkSubscription(string SocialNetworkName, string Token)
        {
            var snNotEx=!(this.SocialNetworks.Exists(s => s.Name == SocialNetworkName));
            //Checks if this social network exists, otherwise it is created
            if (snNotEx)
                this.SocialNetworks.Add(new Storage.EntityModels.SocialNetwork() { Name = SocialNetworkName });
            //Get a reference to the social network, then through Subscribers it adds a new subscription
            var sN = this.SocialNetworks.Find(s => s.Name == SocialNetworkName);
            sN.Subscribers.Add(
                new Storage.EntityModels.SocialNetworkSubscribeDetails() { UserToken = Token });
        }

        /// <summary>
        /// Get an amonymous avatar for the user based on the setted gender
        /// </summary>
        /// <returns>the path of the default avatar</returns>
        public string GetDefaultAvatar()
        {
            switch (this.Gender)
            {
                case Gender.Male:
                    return defaultMalePath;
                case Gender.Female:
                    return defaultFemalePath;
                case Gender.Unknown:
                    return defaultUnknownPath;
                default:
                    return defaultMalePath;
            }
        }

        #endregion

        #region SUPPORT_METHODS

        private void loadBannedList()
        {
            List<int> tmp = StorageManager.Instance.GetBannedUsersByUserId(_Id);
            _Banned = new List<Profile>();
            Profile p;
            Storage.EntityModels.User u;
            foreach (int i in tmp)
            {
                u = StorageManager.Instance.GetUserById(i);
                p = new Profile(u, StorageManager.Instance.GetPrivacyLevels(i), PrivacyLevel.High);
                _Banned.Add(p);
            }
        }

        /// <summary>
        /// Force reloading of BuddiesList.
        /// Called exclusively from User on AddFriend/RemoveFriend
        /// </summary>
        internal void resync_buddies()
        {

            _BuddiesList = null;
            _Banned = null;
        }

        private void loadBuddyList()
        {
            List<int> tmp = StorageManager.Instance.GetFriendsByUserId(_Id);
            _BuddiesList = new List<Profile>();

            Storage.EntityModels.User u;
            Profile p;
            foreach (int i in tmp)
            {
                u = StorageManager.Instance.GetUserById(i);
                p = new Profile(u, StorageManager.Instance.GetPrivacyLevels(i), PrivacyLevel.High);
                _BuddiesList.Add(p);
            }
        }

        internal void refreshLevels()
        {
            foreach (KeyValuePair<string, PrivacyLevel> p in modifiedLevels)
                userData[p.Key] = p.Value;

            modifiedLevels = new Dictionary<string, PrivacyLevel>();
        }

        #endregion
    }
}